package com.jie.component.mongo.wrapper;

import com.jie.component.mongo.util.MongoUtil;
import lombok.Getter;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Criteria;
import com.jie.component.mongo.util.MongoUtil.SFunction;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;

@Getter
public class LambdaMongoQueryWrapper<T> {

    private final List<Criteria> criteriaList = new ArrayList<>();

    private final List<Sort.Order> orders = new ArrayList<>();

    public <R> LambdaMongoQueryWrapper<T> eq(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).is(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> ne(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).ne(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> gt(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).gt(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> gte(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).gte(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> lt(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).lt(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> lte(SFunction<T, R> func, Object value) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).lte(value));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> in(SFunction<T, R> func, Collection<?> values) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).in(values));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> like(SFunction<T, R> func, String value) {
        String fieldName = MongoUtil.getFieldName(func);
        String pattern = value.replace(".", "\\.").replace("*", "\\*")
                .replace("+", "\\+").replace("?", "\\?");
        criteriaList.add(Criteria.where(fieldName).regex(".*" + pattern + ".*", "i"));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> likeLeft(SFunction<T, R> func, String value) {
        String fieldName = MongoUtil.getFieldName(func);
        String pattern = value.replace(".", "\\.").replace("*", "\\*")
                .replace("+", "\\+").replace("?", "\\?");
        criteriaList.add(Criteria.where(fieldName).regex(".*" + pattern, "i"));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> likeRight(SFunction<T, R> func, String value) {
        String fieldName = MongoUtil.getFieldName(func);
        String pattern = value.replace(".", "\\.").replace("*", "\\*")
                .replace("+", "\\+").replace("?", "\\?");
        criteriaList.add(Criteria.where(fieldName).regex(pattern + ".*", "i"));
        return this;
    }

    @SafeVarargs
    public final <R> LambdaMongoQueryWrapper<T> orderByDesc(SFunction<T, R>... funcs) {
        for (SFunction<T, R> func : funcs) {
            String fieldName = MongoUtil.getFieldName(func);
            orders.add(Sort.Order.desc(fieldName));
        }
        return this;
    }

    @SafeVarargs
    public final <R> LambdaMongoQueryWrapper<T> orderByAsc(SFunction<T, R>... funcs) {
        for (SFunction<T, R> func : funcs) {
            String fieldName = MongoUtil.getFieldName(func);
            orders.add(Sort.Order.asc(fieldName));
        }
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> nin(SFunction<T, R> func, Collection<?> values) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).nin(values));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> regex(SFunction<T, R> func, String pattern) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).regex(pattern));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> exists(SFunction<T, R> func, boolean exists) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).exists(exists));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> type(SFunction<T, R> func, int type) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).type(type));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> size(SFunction<T, R> func, int size) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).size(size));
        return this;
    }

    public <R> LambdaMongoQueryWrapper<T> all(SFunction<T, R> func, Collection<?> values) {
        String fieldName = MongoUtil.getFieldName(func);
        criteriaList.add(Criteria.where(fieldName).all(values));
        return this;
    }

}